--constants TRACK_SWITCH_DELTA = 3000 -- switch between tracks delta MAX_DIST = 100 -- max distance for dynamic sound stopping MIN_DIST = 75 -- min distance for dynamic sound playing THEME_FADE_UPDATE_DELTA = 100 -- update theme volume fade twice per second AMBIENT_FADE_UPDATE_DELTA = 200 -- update ambient volume fade twice per second VOLUME_DELTA = 0 -- sound volume fade delta (is set on initialize) -- global variables (used from other scripts) npc_table = {} -- local variables (reinit on initialize()) ambient_vol = get_console_cmd(2,"snd_volume_music") m_ambient_vol = 0 FadeTo_ambient = 0 m_theme_volume = 0 FadeTo_theme = 0 cur_theme_number = 0 cur_track_number = 0 next_track_start_time = 0 theme = nil -- local variables local init_failed = false local themes_inited = false local prev_fade_time = 0 local force_fade = false local was_in_silence = false local _tmr local combat_themes ={ { "music\\combat\\theme1_part_1", "music\\combat\\theme1_part_2", "music\\combat\\theme1_part_3" }, { "music\\combat\\theme2_part_1", "music\\combat\\theme2_part_2", "music\\combat\\theme2_part_3" }, { "music\\combat\\theme3_part_1", "music\\combat\\theme3_part_2", "music\\combat\\theme3_part_3" }, { "music\\combat\\theme4_part_1", "music\\combat\\theme4_part_2", "music\\combat\\theme4_part_3" } } -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- feature_is_active = nil local function main_loop() local tg = time_global() if (_tmr and tg < _tmr) then return false end _tmr = tg + 1000 if not (db.actor) then return false end if (init_failed) then return true -- destroy add_call end if (surge_manager.sound_started()) then if (surge_manager.is_killing_all()) then force_fade = true FadeTo_ambient = ambient_vol fade_ambient() force_fade = false else FadeTo_ambient = 0 fade_ambient() end end if not (themes_inited) then initialize_themes() end if (theme) then theme:update(m_theme_volume) end local state = get_theme_state() if (state=="start") then start_theme() elseif (state=="idle") then if (theme_is_fading()) then fade_theme() elseif (ambient_is_fading()) then fade_ambient() end if (time_global()>next_track_start_time) then select_next_track() end elseif (state=="finish") then finish_theme() end return false end function activate_feature() if (feature_is_active) then return end feature_is_active = true AddUniqueCall(main_loop) RegisterScriptCallback("on_game_end",on_actor_destroy) end function deactivate_feature() if not (feature_is_active) then return end feature_is_active = false RemoveUniqueCall(main_loop) UnregisterScriptCallback("on_game_end",on_actor_destroy) finish_theme() end function on_game_start() if (IsDynamicMusic()) then activate_feature() end end function initialize_themes() m_ambient_vol = ambient_vol m_theme_volume = ambient_vol FadeTo_theme = ambient_vol FadeTo_ambient = ambient_vol cur_theme_number = 0 cur_track_number = 0 next_track_start_time = 0 theme = nil VOLUME_DELTA = m_ambient_vol/50 themes_inited = true end function on_actor_destroy() if (theme) and (theme:playing()) then theme:stop() end exec_console_cmd("snd_volume_music "..ambient_vol) end function main_menu_on() exec_console_cmd("snd_volume_music "..ambient_vol) end function main_menu_off() ambient_vol = get_console_cmd(2,"snd_volume_music") if (IsDynamicMusic()) then activate_feature() exec_console_cmd("snd_volume_music "..m_ambient_vol) else deactivate_feature() end end function get_theme_state() local actor = db.actor force_fade = false if not (actor:alive()) then if(theme) then FadeTo_theme = 0 FadeTo_ambient = ambient_vol if(theme_is_fading() or ambient_is_fading()) then return "idle" else exec_console_cmd("snd_volume_music "..ambient_vol) return "finish" end end return nil end if (actor_in_silence_zone()) then if(theme) then was_in_silence = true FadeTo_theme = 0 FadeTo_ambient = 0 return "idle" end return nil end if(was_in_silence) then was_in_silence = false FadeTo_ambient = ambient_vol end local has_enemy for id,v in pairs(xr_combat_ignore.fighting_with_actor_npcs) do local npc = db.storage[id] and db.storage[id].object if (npc and IsStalker(npc)) then has_enemy = true break end end if not (has_enemy) then if(theme) then FadeTo_theme = 0 FadeTo_ambient = ambient_vol if(theme_is_fading() or ambient_is_fading()) then return "idle" else exec_console_cmd("snd_volume_music "..ambient_vol) return "finish" end end return nil end force_fade = true FadeTo_theme = ambient_vol FadeTo_ambient = 0 if(theme) then return "idle" else return "start" end end function theme_is_fading() return m_theme_volume~=FadeTo_theme end function ambient_is_fading() return m_ambient_vol~=FadeTo_ambient end function actor_in_silence_zone() local silence_zones = db.storage.silence_zone_table if (silence_zones) then local actor_pos = db.actor:position() for k,v in pairs(silence_zones) do local zone = db.zone_by_name[v] if(zone:inside(actor_pos)) then return true end end end if (utils_obj.npc_in_zone(db.actor, "zat_a2_sr_no_assault")) then return true end if (utils_obj.npc_in_zone(db.actor, "jup_a6_sr_no_assault")) then return true end if (utils_obj.npc_in_zone(db.actor, "jup_b41_sr_no_assault")) then return true end return false end function start_theme() --utils_data.debug_write("xrs_dyn_music:start_theme start") m_ambient_vol = 0 exec_console_cmd("snd_volume_music "..m_ambient_vol) m_theme_volume = ambient_vol cur_theme_number = math.random(1, #combat_themes) cur_track_number = math.random(1, #combat_themes[cur_theme_number]) if(theme==nil) then theme = this.stereo_sound() end theme:initialize(combat_themes[cur_theme_number][cur_track_number], m_theme_volume) next_track_start_time = theme:play() - TRACK_SWITCH_DELTA theme:update(m_theme_volume) --utils_data.debug_write("xrs_dyn_music:start_theme end") end function fade_theme() local g_time = time_global() if (g_time-prev_fade_time<=THEME_FADE_UPDATE_DELTA) then return end prev_fade_time = g_time FadeTo_theme = clamp(FadeTo_theme, 0, ambient_vol) if (m_theme_volume>FadeTo_theme) then if (force_fade) then m_theme_volume = FadeTo_theme else m_theme_volume = m_theme_volume - VOLUME_DELTA end m_theme_volume = clamp(m_theme_volume, FadeTo_theme, m_theme_volume) elseif (m_theme_volumeFadeTo_ambient) then if (force_fade) then m_ambient_vol = FadeTo_ambient else m_ambient_vol = m_ambient_vol - VOLUME_DELTA end m_ambient_vol = clamp(m_ambient_vol, FadeTo_ambient, m_ambient_vol) elseif (m_ambient_volcur_theme_number="..tostring(cur_theme_number)) log("-------->cur_track_number="..tostring(cur_track_number)) log("-------->theme="..tostring(theme~=nil)) if (theme) then log("-------->theme is playing="..tostring(theme:playing()~=nil)) end log("-------->cur_state="..tostring(get_theme_state())) printf("wrong theme number") end if (cur_track_number<#combat_themes[cur_theme_number]) then cur_track_number = cur_track_number + 1 else cur_track_number = 1 end if (theme) then next_track_start_time = theme:play_at_time(next_track_start_time + TRACK_SWITCH_DELTA,combat_themes[cur_theme_number][cur_track_number],m_theme_volume) - TRACK_SWITCH_DELTA end end function finish_theme() if (theme) and (theme:playing()) then theme:stop() end themes_inited = false end ----------------------------- class "stereo_sound" function stereo_sound:__init() self.both = nil self.end_time=nil end function stereo_sound:initialize(sound,lvl) if self.both then self:stop() end self.both = sound_object(sound) self.end_time = nil if not self.both then abort("stereo_sound:initialize: Cannot open sound file "..sound) return end if lvl then self:set_volume(lvl) end end function stereo_sound:play() if not db.actor then return end self.both:play(db.actor,0, sound_object.s2d) self.end_time = time_global() + self.both:length() return self.end_time end function stereo_sound:play_at_time(time, sound, lvl) self.end_time = nil self.both:attach_tail(sound) if lvl then self:set_volume(lvl) end self.both_tail = sound_object(sound) if not self.both_tail then abort("stereo_sound:initialize: Cannot open sound file "..sound) return end self.end_time = time + self.both_tail:length() return self.end_time end function stereo_sound:playing() return self.both and self.both:playing() end function stereo_sound:update(vol) if vol and self:playing() then self:set_volume(vol)end end function stereo_sound:stop() if self.both and self.both:playing() then self.both:stop() end end function stereo_sound:length() return self.both and self.both:length() or 1 end function stereo_sound:set_volume(num) if self.both then self.both.volume=num end end